package com.liannong.service;

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.client.RestTemplate;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 区块链存储服务
 * 实现双引擎算法，根据区块数量和交易数量自动切换区块分组
 * 数据在链上只保留14天
 */
@Slf4j
@Service
public class BlockchainStorageService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @Autowired
    private BlockchainService blockchainService;
    
    // 区块链API地址
    private static final String BLOCKCHAIN_API_URL = "http://localhost:5002";
    
    // 存储天数限制(14天)
    private static final int STORAGE_DAYS_LIMIT = 14;
    
    // 阈值配置
    private int blockCountThreshold = 1000; // 区块数量阈值
    private int transactionCountThreshold = 5000; // 交易数量阈值
    
    // 存储区块组信息的Map，键为组ID，值为组信息
    private final Map<String, BlockGroup> blockGroups = new ConcurrentHashMap<>();
    
    // 当前活跃组ID
    private String currentActiveGroupId;
    
    /**
     * 初始化方法
     */
    public void init() {
        // 获取当前区块链状态，确定当前活跃分组
        initializeBlockGroups();
        log.info("区块链存储服务初始化完成，当前活跃分组：{}", currentActiveGroupId);
    }
    
    /**
     * 初始化区块分组
     */
    private void initializeBlockGroups() {
        try {
            // 模拟数据，不实际调用API
            log.info("初始化区块分组，使用模拟数据");
            
            // 创建默认分组
            String defaultGroupId = "group_" + System.currentTimeMillis();
            long currentTime = System.currentTimeMillis();
            
            BlockGroup defaultGroup = new BlockGroup(
                    defaultGroupId,
                    currentTime,
                    120, // 模拟区块数
                    350  // 模拟交易数
            );
            
            blockGroups.put(defaultGroupId, defaultGroup);
            currentActiveGroupId = defaultGroupId;
            
            // 添加一些历史分组用于展示
            for (int i = 1; i <= 3; i++) {
                String oldGroupId = "group_" + (currentTime - i * 3 * 24 * 3600 * 1000L);
                long creationTime = currentTime - i * 3 * 24 * 3600 * 1000L;
                int blockCount = 800 + i * 100;
                int transactionCount = 4200 + i * 300;
                
                BlockGroup oldGroup = new BlockGroup(
                        oldGroupId,
                        creationTime,
                        blockCount,
                        transactionCount
                );
                
                blockGroups.put(oldGroupId, oldGroup);
            }
            
            /* 注释实际API调用
            // 调用区块链API获取现有分组信息
            Map<String, Object> response = restTemplate.getForObject(
                    BLOCKCHAIN_API_URL + "/getBlockGroups", 
                    Map.class);
            
            if (response != null && response.containsKey("groups")) {
                List<Map<String, Object>> groups = (List<Map<String, Object>>) response.get("groups");
                
                for (Map<String, Object> group : groups) {
                    String groupId = (String) group.get("groupId");
                    long creationTime = Long.parseLong(group.get("creationTime").toString());
                    int blockCount = Integer.parseInt(group.get("blockCount").toString());
                    int transactionCount = Integer.parseInt(group.get("transactionCount").toString());
                    
                    BlockGroup blockGroup = new BlockGroup(
                            groupId, 
                            creationTime, 
                            blockCount, 
                            transactionCount);
                    
                    blockGroups.put(groupId, blockGroup);
                    
                    // 确定当前活跃组
                    if (Boolean.TRUE.equals(group.get("active"))) {
                        currentActiveGroupId = groupId;
                    }
                }
            }
            
            // 如果没有活跃组，创建一个新的
            if (currentActiveGroupId == null) {
                createNewBlockGroup();
            }
            */
        } catch (Exception e) {
            log.error("初始化区块分组失败", e);
            // 创建默认分组
            String defaultGroupId = "default_group_" + System.currentTimeMillis();
            BlockGroup defaultGroup = new BlockGroup(defaultGroupId, System.currentTimeMillis(), 0, 0);
            blockGroups.put(defaultGroupId, defaultGroup);
            currentActiveGroupId = defaultGroupId;
        }
    }
    
    /**
     * 创建新的区块分组
     * @return 新分组ID
     */
    private String createNewBlockGroup() {
        try {
            String newGroupId = "group_" + System.currentTimeMillis();
            long currentTime = System.currentTimeMillis();
            
            log.info("创建新区块分组(模拟): {}", newGroupId);
            
            // 添加到本地缓存，不实际调用API
            BlockGroup newGroup = new BlockGroup(newGroupId, currentTime, 0, 0);
            blockGroups.put(newGroupId, newGroup);
            
            // 设置为当前活跃组
            currentActiveGroupId = newGroupId;
            
            /* 注释实际API调用
            // 调用区块链API创建新分组
            Map<String, Object> params = new HashMap<>();
            params.put("groupId", newGroupId);
            params.put("creationTime", currentTime);
            
            restTemplate.postForObject(
                    BLOCKCHAIN_API_URL + "/createBlockGroup",
                    params,
                    Map.class);
            */
            
            return newGroupId;
        } catch (Exception e) {
            log.error("创建新区块分组失败", e);
            throw new RuntimeException("创建新区块分组失败", e);
        }
    }
    
    /**
     * 获取当前活跃的区块分组ID
     * @return 当前活跃分组ID
     */
    public String getCurrentActiveGroupId() {
        if (currentActiveGroupId == null) {
            initializeBlockGroups();
        }
        return currentActiveGroupId;
    }
    
    /**
     * 根据区块数量和交易数量智能切换分组
     * 当前活跃分组的区块数量或交易数量超过阈值时，创建新分组
     * 这是双引擎算法的核心实现
     */
    public void switchBlockGroupIfNeeded() {
        try {
            BlockGroup currentGroup = blockGroups.get(currentActiveGroupId);
            
            if (currentGroup == null) {
                log.warn("当前活跃分组不存在: {}", currentActiveGroupId);
                createNewBlockGroup();
                return;
            }
            
            // 更新当前分组状态
            updateBlockGroupStatus(currentGroup);
            
            // 检查是否需要切换分组（双引擎算法）
            boolean needSwitch = currentGroup.getBlockCount() > blockCountThreshold ||
                               currentGroup.getTransactionCount() > transactionCountThreshold;
            
            if (needSwitch) {
                log.info("当前分组 {} 已达阈值 (区块数: {}, 交易数: {}), 创建新分组",
                        currentActiveGroupId, 
                        currentGroup.getBlockCount(), 
                        currentGroup.getTransactionCount());
                
                String newGroupId = createNewBlockGroup();
                
                // 使用模拟数据，不实际调用API
                /* 注释实际API调用
                // 更新当前分组为非活跃
                Map<String, Object> params = new HashMap<>();
                params.put("groupId", currentActiveGroupId);
                params.put("active", false);
                
                restTemplate.postForObject(
                        BLOCKCHAIN_API_URL + "/updateBlockGroup",
                        params,
                        Map.class);
                */
                
                // 更新当前活跃分组
                currentActiveGroupId = newGroupId;
            }
        } catch (Exception e) {
            log.error("切换区块分组失败", e);
        }
    }
    
    /**
     * 手动切换区块分组
     * 创建新的分组并将当前分组设为非活跃
     * 
     * @return 新分组ID
     */
    public String manualSwitchBlockGroup() {
        try {
            String oldGroupId = currentActiveGroupId;
            
            // 创建新分组
            String newGroupId = createNewBlockGroup();
            
            // 使用模拟数据，不实际调用API
            /* 注释实际API调用
            // 将旧分组设为非活跃
            if (oldGroupId != null) {
                Map<String, Object> params = new HashMap<>();
                params.put("groupId", oldGroupId);
                params.put("active", false);
                
                restTemplate.postForObject(
                        BLOCKCHAIN_API_URL + "/updateBlockGroup",
                        params,
                        Map.class);
            }
            */
            
            log.info("手动切换区块分组(模拟)，从 {} 切换到 {}", oldGroupId, newGroupId);
            return newGroupId;
        } catch (Exception e) {
            log.error("手动切换区块分组失败", e);
            throw new RuntimeException("手动切换区块分组失败", e);
        }
    }
    
    /**
     * 激活指定分组
     * 将指定分组设为活跃，并将当前活跃分组设为非活跃
     * 
     * @param groupId 要激活的分组ID
     * @return 是否成功
     */
    public boolean activateBlockGroup(String groupId) {
        try {
            // 检查分组是否存在
            BlockGroup group = blockGroups.get(groupId);
            if (group == null) {
                log.warn("要激活的分组不存在: {}", groupId);
                return false;
            }
            
            // 如果已经是活跃的，直接返回
            if (groupId.equals(currentActiveGroupId)) {
                return true;
            }
            
            // 使用模拟数据，不实际调用API
            /* 注释实际API调用
            // 将当前活跃分组设为非活跃
            if (currentActiveGroupId != null) {
                Map<String, Object> deactivateParams = new HashMap<>();
                deactivateParams.put("groupId", currentActiveGroupId);
                deactivateParams.put("active", false);
                
                restTemplate.postForObject(
                        BLOCKCHAIN_API_URL + "/updateBlockGroup",
                        deactivateParams,
                        Map.class);
            }
            
            // 将指定分组设为活跃
            Map<String, Object> activateParams = new HashMap<>();
            activateParams.put("groupId", groupId);
            activateParams.put("active", true);
            
            restTemplate.postForObject(
                    BLOCKCHAIN_API_URL + "/updateBlockGroup",
                    activateParams,
                    Map.class);
            */
            
            // 更新当前活跃分组
            currentActiveGroupId = groupId;
            
            log.info("激活区块分组成功(模拟): {}", groupId);
            return true;
        } catch (Exception e) {
            log.error("激活区块分组失败: {}", groupId, e);
            return false;
        }
    }
    
    /**
     * 更新区块分组状态
     * @param blockGroup 区块分组
     */
    private void updateBlockGroupStatus(BlockGroup blockGroup) {
        try {
            // 模拟区块增长，不实际调用API
            if (blockGroup.getGroupId().equals(currentActiveGroupId)) {
                // 只增加当前活跃分组的区块和交易数
                int blockIncrease = (int)(Math.random() * 5) + 1; // 随机增加1-5个区块
                int txIncrease = (int)(Math.random() * 20) + 5;    // 随机增加5-25个交易
                
                blockGroup.setBlockCount(blockGroup.getBlockCount() + blockIncrease);
                blockGroup.setTransactionCount(blockGroup.getTransactionCount() + txIncrease);
                
                log.debug("更新区块分组状态(模拟): {}, 区块+{}, 交易+{}", 
                        blockGroup.getGroupId(), blockIncrease, txIncrease);
            }
            
            /* 注释实际API调用
            Map<String, Object> response = restTemplate.getForObject(
                    BLOCKCHAIN_API_URL + "/getBlockGroupStatus?groupId=" + blockGroup.getGroupId(),
                    Map.class);
            
            if (response != null) {
                int blockCount = Integer.parseInt(response.get("blockCount").toString());
                int transactionCount = Integer.parseInt(response.get("transactionCount").toString());
                
                blockGroup.setBlockCount(blockCount);
                blockGroup.setTransactionCount(transactionCount);
            }
            */
        } catch (Exception e) {
            log.error("更新区块分组状态失败: {}", blockGroup.getGroupId(), e);
        }
    }
    
    /**
     * 获取所有区块分组信息
     * 
     * @return 分组信息列表
     */
    public List<Map<String, Object>> getAllBlockGroups() {
        try {
            List<Map<String, Object>> result = new ArrayList<>();
            
            // 先更新所有分组状态
            for (BlockGroup group : blockGroups.values()) {
                updateBlockGroupStatus(group);
            }
            
            // 转换为Map列表
            for (Map.Entry<String, BlockGroup> entry : blockGroups.entrySet()) {
                BlockGroup group = entry.getValue();
                Map<String, Object> groupMap = new HashMap<>();
                
                groupMap.put("groupId", group.getGroupId());
                groupMap.put("creationTime", group.getCreationTime());
                groupMap.put("blockCount", group.getBlockCount());
                groupMap.put("transactionCount", group.getTransactionCount());
                groupMap.put("active", group.getGroupId().equals(currentActiveGroupId));
                
                result.add(groupMap);
            }
            
            // 按创建时间排序，最新的在前面
            result.sort((a, b) -> Long.compare((Long) b.get("creationTime"), (Long) a.get("creationTime")));
            
            return result;
        } catch (Exception e) {
            log.error("获取区块分组列表失败", e);
            return new ArrayList<>();
        }
    }
    
    /**
     * 获取当前阈值设置
     * 
     * @return 阈值配置
     */
    public Map<String, Integer> getThresholds() {
        Map<String, Integer> thresholds = new HashMap<>();
        thresholds.put("blockCount", blockCountThreshold);
        thresholds.put("transactionCount", transactionCountThreshold);
        return thresholds;
    }
    
    /**
     * 更新阈值设置
     * 
     * @param blockCount 区块数量阈值
     * @param transactionCount 交易数量阈值
     */
    public void updateThresholds(int blockCount, int transactionCount) {
        this.blockCountThreshold = blockCount;
        this.transactionCountThreshold = transactionCount;
        log.info("更新阈值设置: 区块阈值={}, 交易阈值={}", blockCount, transactionCount);
    }
    
    /**
     * 删除指定区块分组
     * 只能删除非活跃且已过期的分组
     * 
     * @param groupId 分组ID
     * @return 是否成功
     */
    public boolean deleteBlockGroup(String groupId) {
        try {
            // 检查分组是否存在
            BlockGroup group = blockGroups.get(groupId);
            if (group == null) {
                log.warn("要删除的分组不存在: {}", groupId);
                return false;
            }
            
            // 不能删除当前活跃分组
            if (groupId.equals(currentActiveGroupId)) {
                log.warn("无法删除当前活跃分组: {}", groupId);
                return false;
            }
            
            // 使用模拟数据，不实际调用API
            /* 注释实际API调用
            // 调用区块链API删除分组
            restTemplate.delete(
                    BLOCKCHAIN_API_URL + "/deleteBlockGroup?groupId=" + groupId);
            */
            
            // 从本地缓存中移除
            blockGroups.remove(groupId);
            
            log.info("删除区块分组成功(模拟): {}", groupId);
            return true;
        } catch (Exception e) {
            log.error("删除区块分组失败: {}", groupId, e);
            return false;
        }
    }
    
    /**
     * 获取区块分组的统计信息
     * 
     * @param groupId 分组ID
     * @return 统计信息
     */
    public Map<String, Object> getBlockGroupStats(String groupId) {
        try {
            // 检查分组是否存在
            BlockGroup group = blockGroups.get(groupId);
            if (group == null) {
                log.warn("分组不存在: {}", groupId);
                return null;
            }
            
            // 更新分组状态
            updateBlockGroupStatus(group);
            
            // 使用模拟数据，不实际调用API
            // 构建基本统计信息
            Map<String, Object> stats = new HashMap<>();
            stats.put("groupId", group.getGroupId());
            stats.put("creationTime", group.getCreationTime());
            stats.put("blockCount", group.getBlockCount());
            stats.put("transactionCount", group.getTransactionCount());
            stats.put("active", group.getGroupId().equals(currentActiveGroupId));
            
            // 添加一些模拟的详细统计信息
            stats.put("avgBlockSize", 2.5 + Math.random()); // 模拟2.5-3.5MB平均区块大小
            stats.put("avgTxPerBlock", 8 + Math.random() * 4); // 模拟8-12笔平均每区块交易数
            stats.put("totalDataSize", group.getBlockCount() * (2.5 + Math.random())); // 模拟总数据大小(MB)
            stats.put("lastBlockTime", System.currentTimeMillis() - (long)(Math.random() * 3600000)); // 最近1小时内
            stats.put("dataFrom", "simulation");
            
            /* 注释实际API调用
            // 获取详细统计信息
            Map<String, Object> response = restTemplate.getForObject(
                    BLOCKCHAIN_API_URL + "/getBlockGroupStats?groupId=" + groupId,
                    Map.class);
            
            if (response == null) {
                // 如果API调用失败，使用本地缓存的基本信息
                Map<String, Object> basicStats = new HashMap<>();
                basicStats.put("groupId", group.getGroupId());
                basicStats.put("creationTime", group.getCreationTime());
                basicStats.put("blockCount", group.getBlockCount());
                basicStats.put("transactionCount", group.getTransactionCount());
                basicStats.put("active", group.getGroupId().equals(currentActiveGroupId));
                basicStats.put("dataFrom", "local");
                
                return basicStats;
            }
            
            // 添加活跃状态
            response.put("active", group.getGroupId().equals(currentActiveGroupId));
            response.put("dataFrom", "remote");
            */
            
            return stats;
        } catch (Exception e) {
            log.error("获取区块分组统计信息失败: {}", groupId, e);
            return null;
        }
    }
    
    /**
     * 定时任务：检查并切换区块分组
     * 每小时执行一次
     */
    @Scheduled(cron = "0 0 * * * ?")
    public void scheduledGroupCheck() {
        log.info("执行定时检查区块分组任务");
        switchBlockGroupIfNeeded();
    }
    
    /**
     * 定时任务：清理过期数据
     * 每天凌晨2点执行
     */
    @Scheduled(cron = "0 0 2 * * ?")
    public void cleanExpiredData() {
        log.info("开始清理过期区块数据(模拟)");
        
        try {
            long cutoffTime = LocalDateTime.now()
                    .minusDays(STORAGE_DAYS_LIMIT)
                    .toEpochSecond(ZoneOffset.UTC) * 1000;
            
            List<String> groupsToRemove = new ArrayList<>();
            
            // 检查每个分组，删除超过保留期的分组
            for (Map.Entry<String, BlockGroup> entry : blockGroups.entrySet()) {
                BlockGroup group = entry.getValue();
                
                // 如果不是当前活跃分组，且创建时间超过保留期
                if (!entry.getKey().equals(currentActiveGroupId) && 
                    group.getCreationTime() < cutoffTime) {
                    
                    // 使用模拟数据，不实际调用API
                    /* 注释实际API调用
                    // 调用区块链API删除分组
                    restTemplate.delete(
                            BLOCKCHAIN_API_URL + "/deleteBlockGroup?groupId=" + group.getGroupId());
                    */
                    
                    groupsToRemove.add(group.getGroupId());
                    log.info("删除过期区块分组(模拟): {}, 创建时间: {}", 
                            group.getGroupId(), new Date(group.getCreationTime()));
                }
            }
            
            // 从本地缓存中移除
            for (String groupId : groupsToRemove) {
                blockGroups.remove(groupId);
            }
            
            log.info("过期区块数据清理完成，共清理 {} 个分组", groupsToRemove.size());
        } catch (Exception e) {
            log.error("清理过期区块数据失败", e);
        }
    }
    
    /**
     * 区块分组内部类
     */
    private static class BlockGroup {
        private final String groupId;
        private final long creationTime;
        private int blockCount;
        private int transactionCount;
        
        public BlockGroup(String groupId, long creationTime, int blockCount, int transactionCount) {
            this.groupId = groupId;
            this.creationTime = creationTime;
            this.blockCount = blockCount;
            this.transactionCount = transactionCount;
        }
        
        public String getGroupId() {
            return groupId;
        }
        
        public long getCreationTime() {
            return creationTime;
        }
        
        public int getBlockCount() {
            return blockCount;
        }
        
        public void setBlockCount(int blockCount) {
            this.blockCount = blockCount;
        }
        
        public int getTransactionCount() {
            return transactionCount;
        }
        
        public void setTransactionCount(int transactionCount) {
            this.transactionCount = transactionCount;
        }
    }
} 